#include <regx52.h>
#include "timer.c"
#include "uart.c"

/**
 * 实现一个打地鼠游戏，随机点亮一个灯，用户按下对应按键。灯灭后，再随机亮灯。
 * 检测游戏失败：设定一个阈值，连续进入几次中断后，发现上一次点亮的灯没有被熄灭，就认定游戏失败
 * 将当前点亮的灯进行闪烁。
 *
 * 增加一个开始按钮，点击开始按钮，游戏重新开始。
 * 如果在游戏进行中，点击该按钮，则暂停游戏，再次点击则恢复游戏。
 * 暂停游戏中，进行流水灯闪烁。
 */

#define LEDS P2
#define BTNS P1

sbit BTN_START = P3 ^ 3;

#define STATE_BEGIN 0
#define STATE_WAITING_USER_INPUT 1
#define STATE_PAUSE 2
#define STATE_END 3

state = STATE_BEGIN;

unsigned char led_light_index = 0xFF;

volatile unsigned int fail_count = 0;
unsigned char user_input_keys;

volatile unsigned int led_flicker_count = 0;

volatile unsigned int led_flowing_count = 0;
volatile unsigned char led_flowing_index = 0;

#define FAIL_COUNT_MAX 200
#define FLICKER_COUNT_MAX 50
#define FLOWING_COUNT_MAX 50

void delay_ms(unsigned int ms)
{
    unsigned int i, j;
    for (i = 0; i < ms; i++)
    {
        for (j = 0; j < 123; j++)
            ;
    }
}

/**
 * 当没有按键按下，返回0xFF
 * 按下返回0
 */
unsigned char btn_start_scan()
{
    if (BTN_START == 0)
    {
        delay_ms(20);
        if (BTN_START == 0)
        {
            while (BTN_START == 0)
                ;
            return 0;
        }
    }
    return 0xFF;
}

void timer0_ISR() interrupt 1
{
    static bit led_flicker_on = 0;
    // 11.0592  10ms
    TH0 = 0xDB;
    TL0 = 0xFF;

    if (state == STATE_WAITING_USER_INPUT)
    {
        fail_count++;

        if (fail_count > FAIL_COUNT_MAX)
        {
            state = STATE_END;
            fail_count = 0;
        }
    }

    if (state == STATE_END)
    {
        led_flicker_count++;
        if (led_flicker_count > FLICKER_COUNT_MAX)
        {
            led_flicker_count = 0;
            if (led_flicker_on)
            {
                LEDS = 0xFF;
                led_flicker_on = 0;
            }
            else
            {
                led_flicker_on = 1;
                LEDS = ~(0x01 << led_light_index);
            }
        }
    }

    if (state == STATE_PAUSE)
    {
        led_flowing_count++;
        if (led_flowing_count > FLOWING_COUNT_MAX)
        {
            led_flowing_count = 0;
            LEDS = (0x01 << led_flowing_index);
            led_flowing_index++;
            if (led_flowing_index >= 8)
            {
                led_flowing_index = 0;
            }
        }
    }
}

unsigned char get_key_index()
{
    unsigned char i;
    for (i = 0; i < 8; i++)
    {
        if ((user_input_keys & (0x01 << i)) == 0)
        {
            printf("key index=%d\n", (int)i);
            return i;
        }
    }
    return 0xF0;
}

void main()
{
    timer0_init();
    uart_init();
    timer0_isr_on();
    while (1)
    {
        switch (state)
        {
        case STATE_BEGIN:
            if (btn_start_scan() != 0xFF)
            {
                state = STATE_WAITING_USER_INPUT;
                timer_seed = get_timer0_value();
            }
            break;
        case STATE_WAITING_USER_INPUT:
            if (led_light_index == 0xFF)
            {
                led_light_index = timer_rand() % 8;
                LEDS = ~(0x01 << led_light_index);
            }
            if (BTNS != 0xFF)
            {
                delay_ms(20);
                if (BTNS != 0xFF)
                {
                    user_input_keys = BTNS;
                }
                while (BTNS != 0xFF)
                    ;
                if (led_light_index == get_key_index())
                {
                    led_light_index = 0xFF;
                    fail_count = 0;
                }
            }
            if (btn_start_scan() != 0xFF)
            {
                state = STATE_PAUSE;
            }
            break;
        case STATE_END:
            if (btn_start_scan() != 0xFF)
            {
                state = STATE_BEGIN;
                LEDS = 0xFF;
                led_light_index = 0xFF;
                fail_count = 0;
            }
            break;
        case STATE_PAUSE:
            if (btn_start_scan() != 0xFF)
            {
                state = STATE_WAITING_USER_INPUT;
                LEDS = ~(0x01 << led_light_index);
            }
            break;
        default:
            break;
        }
    }
}
